package com.zimo.download;


import com.zimo.download.info.PointerOffset;
import com.zimo.download.info.event.EventHandlerS;
import com.zimo.download.info.event.EventSet;
import com.zimo.utils.Connection;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;


/**
 * 这是一个下载实现类，里面封装了真正可以下载网络资源的类
 * 这个类会发出Range请求头，所以如果你想用单线程下载的话那么他不是很好的选择
 */
public class DownloadImpl implements Download {

    //获取logger对象： 传入当前类的class，这样代表这个类输出的日志信息
    private Logger logger = Logger.getLogger(DownloadImpl.class);

    //停止下载标志
    private  String stopFile = "";


    private int restartCount = 7;//下载该文件可以尝试恢复次数，注意：所以的共同的下载器对象线程共用同一个次数
    private int finishThread = 0;//线程（死亡）下载完成数

    private long downloadingFileSize = 0;//已经下载的文件大小
    private long oldDownloadingFileSize = 0;//已经下载的文件大小
    private long time = 0;//当前时间戳，用来计时的当过了1s后就发出一次速度事件

    @Override
    public boolean startDownload(URL url, String filePath,PointerOffset pointerOffset){
        String threadName = Thread.currentThread().getName();
        RandomAccessFile raf = null;   //本地文件流
        InputStream in = null;  //网络文件流
        DownloadCacheImpl downloadCache =null;//缓存断点流
        try{
            //获取一个请求头Range (作用告诉服务器每个线程下载的开始位置和结束位置)
            HttpURLConnection con = Connection.getConnection(url, false);
            con.setRequestProperty("Range", "bytes="+pointerOffset.getSTART()+"-"+pointerOffset.getEND());
            //打印日志
            logger.info("开始下载: "+pointerOffset.getSTART()+"-"+pointerOffset.getEND());
            //创建随机读写文件对象
            raf = new RandomAccessFile(filePath, "rw");
            //当前线程这个位置开始写
            raf.seek(pointerOffset.getSTART());


            //缓存断点流初始化
            downloadCache = new DownloadCacheImpl(url.toString(),filePath,Thread.currentThread().getName());
            //网络输入流,判断Range是否正常
            if((pointerOffset.getSTART() < pointerOffset.getEND()) && (-1 < (pointerOffset.getSTART() + pointerOffset.getEND()))) in =con.getInputStream();
            //追踪下载进度/断点
            long start =pointerOffset.getSTART();//获取文件开始位置
             time = System.currentTimeMillis();//方案1变种
            //把数据写到文件中
            int len = -1;//数据包的大小
            byte[] buffer = new byte[65515];//缓存区
            while(in != null && (len = in.read(buffer))!=-1 ){
                raf.write(buffer, 0, len);//下载网络文件到本地
                //更新进度
                start= start+len;
                //输出断点
                downloadCache.setBreakpoint(start,pointerOffset.getEND(),filePath);
                pointerOffset.setSTART(start);


                //响应当前线程下载速度监听,单位byte
                //更新慢，精度高
                synchronized (this){
                    downloadingFileSize+=len;
                    if ((System.currentTimeMillis() - time) >= 1000) {
                        time=System.currentTimeMillis();
                        //获取事件，并发出事件(如果事件不为null)
                        EventHandlerS eventSpeed = EventSet.getEventSet().getEventSpeed(url);
                        if(eventSpeed != null) eventSpeed.handle(downloadingFileSize-oldDownloadingFileSize,threadName);
                        oldDownloadingFileSize=downloadingFileSize;
                    }
                }


                //停止下载标志成立,停止下载
                if ((threadName.indexOf(stopFile+" --- ") != -1) && (!stopFile.equals(""))){
                    synchronized(this.getClass()){
                        logger.info(stopFile+" 下载被暂停");
                        //响应暂停事件
                        EventHandlerS eventStop = EventSet.getEventSet().getEventStop(url);
                        if (eventStop != null) eventStop.handle("线程暂停",threadName);
                        break;
                    }
                }
            }
        }catch (Exception e){
//            e.printStackTrace();

            //出现异常，尝试恢复下载，如果恢复次数没了就只能结局线程
            if (restartCount > 0){
                //抛出线程异常
                logger.error("线程下载文件出现异常,具体原因: ",e);
                logger.error("线程出现异常意外停止下载，正在重新恢复下载。该下载器可尝试次数: "+restartCount--);
                return startDownload(url, filePath, pointerOffset);
            }else {
                //下载时异常出现，停止下载并调用下载完成事件
                EventHandlerS eventFinish = EventSet.getEventSet().getEventFinish(url);
                if (eventFinish != null) eventFinish.handle(false,threadName);
                finishThread++;//完成线程数+1
                return false;
            }
            //返回false,同时结束线程，并执行 finally 语句块的内容(不执行非 finally 语句的内容)
        }finally {
            //关闭网络流
            if (in!=null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.error(Thread.currentThread().getName()+"线程未能成功关闭"+in);
                }
            }
            //关闭本地流
            if (raf!=null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    logger.error(Thread.currentThread().getName()+"线程未能成功关闭"+raf);
                }
            }
            //关闭断点流
            if (downloadCache != null){
                downloadCache.close();
            }

            //响应当前线程下载速度监听(下载完成速度为 -1 byte/ms),单位byte,防止上层拿到不正确的速度
            EventHandlerS eventSpeed = EventSet.getEventSet().getEventSpeed(url);
            if (eventSpeed != null) eventSpeed.handle(0l,threadName);
        }

        //如果线程不是被暂停的而是正常结束的就输出
        if (stopFile.equals("")){
            //输出日志
            logger.info("下载完成 :"+pointerOffset.getSTART() +"-"+pointerOffset.getEND());
        }


        //线程(正常)下载完成调用下载完成事件
        EventHandlerS eventFinish = EventSet.getEventSet().getEventFinish(url);
        if (eventFinish != null) eventFinish.handle(true,threadName);
        finishThread++;//完成线程数+1

        return true;
    }


    @Override
    public boolean stopDownload(String name) {
        this.stopFile=name;

        logger.info(name+"下载即将被停止....");
        return true;
    }

    public int getFinishThread() {
        return finishThread;
    }

    @Override
    public long getDownloadingFileSize() {
        return downloadingFileSize;
    }
}
